package com.similarworld.dao;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.similarworld.model.AbstractModel;

@SuppressWarnings("unchecked")
public abstract class AbstractDAO< T extends AbstractModel > {

	private static final Logger logger = LoggerFactory.getLogger(AbstractDAO.class);

	private Class< T > persistentClass;

	@PersistenceContext
	EntityManager entityManager;

	/**
	 * Sinifin bir ornegi elde edilir edilmez, modelin class bilgisi belirlenir
	 */
	
	public AbstractDAO() {
		super();
		ParameterizedType pt = null;

		try {
			pt = (ParameterizedType) getClass().getGenericSuperclass();
		} catch (ClassCastException cce) { 
			//kendisinde generic tip olmayabilir ama mutlaka bir ust sinifinda vardir!
			pt = (ParameterizedType) getClass().getSuperclass().getGenericSuperclass();
		}

		persistentClass = (Class<T>) pt.getActualTypeArguments()[0];
	}

	public T get( final Long id ){
		return entityManager.find( persistentClass, id );
	}
	
	public T findFirstByKeyValue(String key, Object value) {
		logger.debug("findFirstByKeyValue metodu cagrildi, {key -> " + key + 
													 ", Object -> " + value + "}");

		if (key == null || key.trim().isEmpty() || value == null) {
			logger.warn("key veya value degeri null!");
			return null;
		}
		
		T result = null;
		
		try {
			List<T> resultList = 
				(List<T>) entityManager.createQuery(
								"from " + persistentClass.getSimpleName() + " m " +
							  	"where m." + key + " = :" + key )
						.setParameter(key, value)
					.getResultList();
			if (resultList != null && resultList.size() > 0) {
				result = resultList.get(0);
				
				if (resultList.size() > 1) {
					logger.warn("Dikkat : Veritabaninda ayni sartlara uyan " + (resultList.size() - 1) +  
							" tane daha kayit var!");
				}
			}
			
		} catch (Exception e) {
			logger.error("ERROR : ", e);
		}
		
		return result;
	}

	public List< T > getAll(){
		return entityManager.createQuery( "from " + persistentClass.getName() )
				.getResultList();
	}

	@Transactional
	public void save( final T entity ){
		entityManager.persist( entity );
	}
	
	@Transactional
	public void saveAnyModel( final AbstractModel entity ){
		entityManager.persist( entity );
	}

	@Transactional
	public T update( final T entity ){
		return entityManager.merge( entity );
	}
	
	@Transactional
	public AbstractModel mergeAnyModel( final AbstractModel entity ){
		return entityManager.merge( entity );
	}

	@Transactional
	public void delete( final T entity ){
		entityManager.remove( entity );
	}

	@Transactional
	public int deleteById( Long id ){
		return entityManager.createQuery("delete from " + persistentClass.getSimpleName() + " m " +
				  "where m.id = :id")
			.setParameter("id", id)
		.executeUpdate();
	}
}
